/*

Frequency of use: [1] 2 3 4 5 LOW

Short:
    State is like an enum, but we can define different behaviour for each state. 
We could add another class, Sad, which did something entirely different when 
asked to talk().

Long:
    In object-oriented programming and software engineering, the visitor design 
pattern is a way of separating an algorithm from an object structure upon which 
it operates. A practical result of this separation is the ability to add new 
operations to existing object structures without modifying those structures. 
Thus, using the visitor pattern helps conformance with the open/closed 
principle.
In essence, the visitor allows one to add new virtual functions to a family of 
classes without modifying the classes themselves; instead, one creates a visitor 
class that implements all of the appropriate specializations of the virtual 
function. The visitor takes the instance reference as input, and implements the 
goal through double dispatch.
While powerful, the visitor pattern is more limited than conventional virtual 
functions. It is not possible to create visitors for objects without adding a 
small callback method inside each class and the callback method in each of the 
classes is not inheritable

Information sources:
 - http://calumgrant.net/patterns/index.html
 - http://en.wikipedia.org/wiki/Visitor_pattern
 - http://www.dofactory.com/Patterns/Patterns.aspx
*/

#include <vector>
#include <string>
#include <iostream>

class Visitor
{
public:
	virtual ~Visitor() { }
	virtual void on_recipient(const std::string &)=0;
};

class Recipients
{
	std::vector<std::string> recipients;
public:
	void add_recipient(const std::string & recipient)
	{
		recipients.push_back(recipient);
	}
	void visit(Visitor & visitor) const
	{
		for(std::vector<std::string>::const_iterator recipient = recipients.begin();
			recipient != recipients.end(); 
			++recipient)
		{
			visitor.on_recipient(*recipient);
		}
	}
};

class PrintHello : public Visitor
{
public:
	void on_recipient(const std::string & recipient)
	{
		std::cout << "Hello " << recipient << "!" << std::endl;
	}
};

void hello_world(Visitor & visitor)
{
	Recipients recipients;
	recipients.add_recipient("world");
	recipients.visit(visitor);
}

int main()
{
	PrintHello visitor;
	hello_world(visitor);
	return 0;
}
